package com.easyaop.asm.bytecode;

import com.easyaop.api.LoggerFactory;
import com.easyaop.asm.bytecode.asm.AroundClassVisitor;
import com.easyaop.asm.bytecode.asm.ClassVisitorReadCode;
import com.easyaop.asm.bytecode.asm.ClassVisitorRemove;
import com.easyaop.asm.bytecode.asm.ReadNameClassVisitor;
import com.easyaop.asm.bytecode.asm.methodvisitor.AroundComplieBuilder;
import com.easyaop.asm.bytecode.asm.methodvisitor.DefaultMethodVisitorCompile;
import org.aspectj.apache.bcel.classfile.ClassParser;
import org.aspectj.apache.bcel.classfile.JavaClass;
import org.objectweb.asm.*;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Logger;

public class ByteUtils implements Opcodes {
    private static final Logger LOGGER = LoggerFactory.getLogger("Transformer");
    static AtomicInteger integer = new AtomicInteger(0);
    int api = Opcodes.ASM9;

    public List<MethodInfo> read(byte[] bytes, String method, String desc, int flag) {

        ClassVisitor cv = null;

        ClassVisitorReadCode classVisitorReadCode = new ClassVisitorReadCode(api, cv);
        classVisitorReadCode.setMethod(method);
        classVisitorReadCode.setDesc(desc);
        cv = classVisitorReadCode;

        ClassReader cr = new ClassReader(bytes);
        cr.accept(cv, flag);

        return classVisitorReadCode.methodInfoList;
    }

    public byte[] around(DefaultQuery query) {
        return around(query, AroundComplieBuilder.to(query.getAopBytes()));
    }

    private byte[] around(DefaultQuery query, DefaultMethodVisitorCompile around) {
        byte[] bytes = query.getBytes();
//        ClassParser parser = new ClassParser(new ByteArrayInputStream(bytes), "Anonymous");

        try {
//            JavaClass parse = parser.parse();
//            System.out.println(parse);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        String method = query.getMethod();
        String descriptor = query.getDescriptor();

        ClassVisitor cv;

        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES)
        {
            @Override
            protected String getCommonSuperClass(String type1, String type2) {
                String objName = "java/lang/Object";
                if(objName.equals(type1)
                        || objName.equals(type2)){
                    return objName;
                }

                String className = query.getClassName().replaceAll("\\.","/");
                if(type1.equals(className)) {
                    LOGGER.warning(type1+" --type1  "+type2);
//                    return type2;
                    return objName;

                }
                if(type2.equals(className)) {
                    LOGGER.warning(type1+" --type2 "+type2);
//                    return type1;
                    return objName;

                }
//if(true)
//                return objName;
                try {
                    return super.getCommonSuperClass(type1, type2);
                } catch (Throwable e) {
                    LOGGER.warning(type1+" -- "+type2);
                    throw new RuntimeException(e);
                }
            }
        }
        ;
        cv = cw;

        List<MethodInfo> methodInfoList = new LinkedList<>();
        AroundClassVisitor aroundClassVisitor = new AroundClassVisitor(api, cv);
        aroundClassVisitor.setAround(around);
        aroundClassVisitor.setMethodInfoList(methodInfoList);
        aroundClassVisitor.setJpPos(query.getJoinPoint());
        cv = aroundClassVisitor;

        ClassVisitorRemove classVisitorRemove = new ClassVisitorRemove(api, cv);
        classVisitorRemove.setMethod(method);
        classVisitorRemove.setDesc(descriptor);
        cv = classVisitorRemove;

        ClassVisitorReadCode classVisitorReadCode = new ClassVisitorReadCode(api, cv);
        classVisitorReadCode.setMethod(method);
        classVisitorReadCode.setDesc(descriptor);
        classVisitorReadCode.methodInfoList = methodInfoList;
        cv = classVisitorReadCode;

        ReadNameClassVisitor readNameClassVisitor = new ReadNameClassVisitor(api, cv);
        cv= readNameClassVisitor;

        new ClassReader(bytes).accept(cv
//                ,0
                , ClassReader.SKIP_FRAMES
        );

        byte[] byteArray = cw.toByteArray();
        try {
            String name = "dump/"+ readNameClassVisitor.getClassName() +"_"+ integer.getAndIncrement() + ".class";
            File file = new File(name);
            File parentDir = file.getParentFile();
            if (!parentDir.exists()) {
                parentDir.mkdirs();
            }
            FileOutputStream outputStream = new FileOutputStream(name);
            outputStream.write(byteArray);
            outputStream.close();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return byteArray;
    }

}
